#include "ArchivoDeEstructurasVariables.h"
#include <sstream>
using namespace Archivos;
using namespace Entidades;

ArchivoDeEstructurasVariables::ArchivoDeEstructurasVariables(string path) :
	Archivo(path) {
}

/* Leo a la entidad que se encuentra desde la posiciónEnElArchivo hasta el tamaño de ella
 * (primeros 4 bytes de la entidad) */
void ArchivoDeEstructurasVariables::leer(IEntidad* entidad,unsigned int posicionEnElArchivo){

	unsigned int tamanioTotal = 0; // buffer + metadata
	unsigned int tamanioDelBuffer = 0;
	char* buffer;

	abrirArchivo();

	//leo el encabezado (metadata) que está al principio de la entidad
	archivo.seekg(posicionEnElArchivo, ios::beg);
	archivo.read((char*)&tamanioTotal, sizeof(int));

	//inicializo el buffer con un tamaño
	tamanioDelBuffer = (tamanioTotal);
	buffer = new char[tamanioDelBuffer];

	//escribo en el buffer
	archivo.read(buffer, tamanioDelBuffer);

	cerrarArchivo();

	//lleno la entidad con el buffer
	entidad->hidratarse(buffer, tamanioDelBuffer);

	//como terminé de usarlo, borro el buffer
	delete [] buffer;
}

/* Inserta a la nueva entidad al final del archivo.
 * Retorna posición de la entidad insertada en el archivo.  */
unsigned int ArchivoDeEstructurasVariables::insertar(IEntidad* entidad) {

	char* buffer = entidad->construirBuffer();
	int tamanioDelBuffer = entidad->getTamanioDelBuffer();
	unsigned int posicionDeEscritura = 0;
	stringstream* ss = new stringstream(stringstream::in | stringstream::out | stringstream::binary);

	//escribo primero el tamaño total de la entidad
	ss->write((char*)&tamanioDelBuffer,sizeof(int));

	//y luego a la entidad
	ss->write(buffer, tamanioDelBuffer);

	abrirArchivo();

	archivo.seekp(0, ios::end);
	posicionDeEscritura = archivo.tellp();
	archivo.write(ss->str().c_str(),tamanioDelBuffer + sizeof(int));

	cerrarArchivo();

	delete ss;
	delete [] buffer;
	return posicionDeEscritura;
}

/* Encuentra a la entidad 'a modificar' y la compara con la 'modificada'
 * --> Si el tamaño de la entidad 'modificada' es menor pisa a la entidad 'a modificar'. Devuelve -1
 * --> Si el tamaño de la entidad 'modificada' es mayor borra la 'a modificar' e inserta la entidad
 * 	   'modificada' al final del archivo. Devuelve la nueva posición de la entidad. */
unsigned int ArchivoDeEstructurasVariables::modificar(IEntidad* entidad,unsigned int posicionEnElArchivo){

	// TODO: hacer la opcion 1

	//inserta a la entidad 'modificada' al final del archivo
	int nuevaPosicionEnElArchivo = this->insertar(entidad);

	//Borra a la entidad antigua
	this->borrar(entidad, posicionEnElArchivo);

	//retorno la nueva posición en el archivo
	return nuevaPosicionEnElArchivo;
}

/* Encuentro la entidad, la marco como borrada y la guardo.
 * Retorno true si el borrado fue exitoso */
bool ArchivoDeEstructurasVariables::borrar(IEntidad* entidad,unsigned int posicionEnElArchivo){

	entidad->marcarComoBorrado();

	char* buffer = entidad->construirBuffer();
	int tamanioDelBuffer = entidad->getTamanioDelBuffer();

	abrirArchivo();

	archivo.seekp(posicionEnElArchivo, ios::beg);
	archivo.write(buffer,tamanioDelBuffer);

	cerrarArchivo();

	delete [] buffer;
	return true;
}

ArchivoDeEstructurasVariables::~ArchivoDeEstructurasVariables() {
}

